/*
 * Copyright (c) 2010 Toni Spets <toni.spets@iki.fi>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "common.h"
#include "launcher.h"
#include "wsock32.h"

void hook_socket(CONTEXT *ctx)
{
    uint32_t af = pop(ctx);
    uint32_t type = pop(ctx);
    uint32_t protocol = pop(ctx);

    ctx->Eax = fake_socket(af, type, protocol);
}

void hook_bind(CONTEXT *ctx)
{
    struct sockaddr name_local;

    uint32_t s = pop(ctx);
    uint32_t *name = (uint32_t *)pop(ctx);
    uint32_t namelen = pop(ctx);

    memread(name, &name_local, namelen);

    ctx->Eax = fake_bind(s, &name_local, namelen);
}

void hook_recvfrom(CONTEXT *ctx)
{
    char buf_local[1024];
    struct sockaddr from_local;
    int fromlen_local;

    uint32_t s = pop(ctx);
    uint32_t *buf = (uint32_t *)pop(ctx);
    uint32_t len = pop(ctx);
    uint32_t flags = pop(ctx);
    uint32_t *from = (uint32_t *)pop(ctx);
    uint32_t *fromlen = (uint32_t *)pop(ctx);

    memread(fromlen, &fromlen_local, 4);

    ctx->Eax = fake_recvfrom(s, buf_local, len, flags, &from_local, &fromlen_local);

    memwrite(buf, buf_local, ctx->Eax);
    memwrite(fromlen, &fromlen_local, 4);
    memwrite(from, &from_local, fromlen_local);

    #ifdef CNCNET_CLIENT
        ThreadingVector<Packet>* incomingPacket = LauncherHooks::incomingPacketInstance();
        Packet packet;
        packet.setType(IncomingPacket);
        //packet.setAddr(addr);
        packet.setMessage(buf_local, ctx->Eax);
        incomingPacket->addData(packet);
    #endif
}

void hook_sendto(CONTEXT *ctx)
{
    char buf_local[1024];
    struct sockaddr to_local;

    SOCKET s = (SOCKET)pop(ctx);
    char *buf = (char *)pop(ctx);
    int len = (int)pop(ctx);
    int flags = (int)pop(ctx);
    struct sockaddr *to = (struct sockaddr *)pop(ctx);
    int tolen = (int)pop(ctx);

    memread(buf, buf_local, len);
    memread(to, &to_local, tolen);

    ctx->Eax = fake_sendto(s, buf_local, len, flags, &to_local, tolen);
    #ifdef CNCNET_CLIENT
        ThreadingVector<Packet>* incomingPacket = LauncherHooks::incomingPacketInstance();
        Packet packet;
        packet.setType(OutgoingPacket);
        //packet.setAddr(addr); // FIXME
        packet.setMessage(buf_local, len);
        incomingPacket->addData(packet);
    #endif
}

void hook_getsockopt(CONTEXT *ctx)
{
    char *optval_local;
    int optlen_local = 0;

    SOCKET s = (SOCKET)pop(ctx);
    int level = (int)pop(ctx);
    int optname = (int)pop(ctx);
    char *optval = (char *)pop(ctx);
    int *optlen = (int *)pop(ctx);

    memread(optlen, &optlen_local, 4);
    optval_local = (char *)malloc(optlen_local);
    memread(optval, optval_local, optlen_local);

    ctx->Eax = fake_getsockopt(s, level, optname, optval_local, &optlen_local);

    memwrite(optlen, &optlen_local, 4);
    memwrite(optval, optval_local, optlen_local);
    free(optval_local);
}

void hook_setsockopt(CONTEXT *ctx)
{
    uint32_t optval_local;

    SOCKET s = (SOCKET)pop(ctx);
    int level = (int)pop(ctx);
    int optname = (int)pop(ctx);
    char *optval = (char *)pop(ctx);
    int optlen = (int)pop(ctx);

    memread(optval, &optval_local, 4);

    ctx->Eax = fake_setsockopt(s, level, optname, (char *)&optval_local, optlen);

    memwrite(optval, &optval_local, 4);
}

/* permission relay functions */
void hook_closesocket(CONTEXT *ctx)
{
    SOCKET s = (SOCKET)pop(ctx);

    ctx->Eax = closesocket(s);
}

void hook_WSAAsyncSelect(CONTEXT *ctx)
{
    SOCKET s = (SOCKET)pop(ctx);
    HWND hWnd = (HWND)pop(ctx);
    unsigned int wMsg = (unsigned int)pop(ctx);
    long lEvent = (long)pop(ctx);

    ctx->Eax = WSAAsyncSelect(s, hWnd, wMsg, lEvent);
}
